/*
客户端的所有请求，都通过该模块进行发送。客户端收到的所有响应，也会先被该模块接收
*/

#pragma once

#include "../Common/Message.hpp"
#include "../Common/Net.hpp"
#include <memory>
#include <functional>
#include <unordered_map>
#include <future>

namespace RpcModule
{
    namespace ClientModule
    {
        class Requestor
        {
        public:
            using ptr = std::shared_ptr<Requestor>;
            using RequestCallBack = std::function<void(const BaseMessage::ptr &)>;
            using AsyncResponse = std::future<BaseMessage::ptr>;
            // 请求描述字段
            struct RequestDescribe
            {
                using ptr = std::shared_ptr<RequestDescribe>;
                // 1. 请求id、请求类型(Rpc请求/应答)
                BaseMessage::ptr request;
                // 2. 异步/回调函数执行策略
                RType rtype;
                // 3. 异步策略下，通过promise和future进行设置/获取结果
                std::promise<BaseMessage::ptr> response;
                // 4. 回调函数策略下，需要有回调函数
                RequestCallBack callback;
            };
            // 注册到dispatcher模块中，一旦客户端收到响应，就会调用该回调函数
            void OnResponse(const BaseConnection::ptr &conn, BaseMessage::ptr &message)
            {
                // 收到响应 -- 1. 异步策略 -- 将结果填到promise中
                // 2. 回调函数策略 -- 调用响应的回调函数
                std::string id = message->GetRid();
                RequestDescribe::ptr describe = GetDescribe(id);
                if (describe.get() == nullptr)
                {
                    LOGERROR("收到响应，但是没有与之对应的请求描述");
                    return;
                }
                if (describe->rtype == RType::REQ_ASYNC)
                {
                    describe->response.set_value(message);
                }
                else if (describe->rtype == RType::REQ_CALLBACK)
                {
                    if (describe->callback)
                        describe->callback(message);
                }
                else
                {
                    LOGERROR("请求类型未知！");
                }
                DeleteDescribe(id);
            }
            // 1. 发送请求 -- 采用异步策略，将结果保存在传入的future中
            bool Send(const BaseConnection::ptr &conn, const BaseMessage::ptr &request, AsyncResponse &async_response)
            {
                // 1. 先构建请求
                RequestDescribe::ptr describe = CreateDescribe(request, RType::REQ_ASYNC);
                if (describe.get() == nullptr)
                {
                    LOGERROR("构建请求描述失败！");
                    return false;
                }
                // 2. 获取结果，结果保存在future中
                async_response = describe->response.get_future();
                // 3. 发送请求
                conn->Send(request);
                return true;
            }
            // 2. 发送请求 -- 回调函数策略，当有结果时，调用回调函数进行结果的处理
            bool Send(const BaseConnection::ptr &conn, const BaseMessage::ptr &request, const RequestCallBack &cb)
            {
                // 1. 先构建请求
                RequestDescribe::ptr describe = CreateDescribe(request, RType::REQ_CALLBACK, cb);
                if (describe.get() == nullptr)
                {
                    LOGERROR("构建请求描述失败！");
                    return false;
                }
                // 2. 发送请求
                conn->Send(request);
                // 3. 如果收到请求，会在OnMessage中直接调用回调函数进行处理
                return true;
            }
            // 3. 发送请求 -- 同步策略，当有结果时，需要阻塞等待接收结果
            bool Send(const BaseConnection::ptr &conn, const BaseMessage::ptr &request, BaseMessage::ptr &rsp)
            {
                // 1. 构造一个future对象，直接通过future进行get，阻塞等待结果即可
                AsyncResponse response;
                bool ret = Send(conn, request, response);
                if (ret == false)
                    return false;
                rsp = response.get();
                return true;
            }

        private:
            // 添加特定请求的描述
            RequestDescribe::ptr CreateDescribe(const BaseMessage::ptr &request, RType rtype,
                                                const RequestCallBack &cb = RequestCallBack())
            {
                RequestDescribe::ptr describe = std::make_shared<RequestDescribe>();
                describe->rtype = rtype;
                describe->request = request;
                if (rtype == RType::REQ_CALLBACK && cb)
                    describe->callback = cb;
                {
                    std::unique_lock<std::mutex> lock(_mutex);
                    _request_desc.insert(std::make_pair(request->GetRid(), describe));
                }
                return describe;
            }
            // 查找特定请求的描述
            RequestDescribe::ptr GetDescribe(const std::string &id)
            {
                std::unique_lock<std::mutex> lock(_mutex);
                auto it = _request_desc.find(id);
                if (it == _request_desc.end())
                    return RequestDescribe::ptr();
                return it->second;
            }
            // 删除特定请求的描述
            void DeleteDescribe(const std::string &id)
            {
                std::unique_lock<std::mutex> lock(_mutex);
                _request_desc.erase(id);
            }

        private:
            std::mutex _mutex;
            // 需要有id和请求描述字段的映射关系
            std::unordered_map<std::string, RequestDescribe::ptr> _request_desc;
        };
    }
}